package com.mikiya.plugin;

import com.intellij.ide.highlighter.JavaFileType;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.pom.Navigatable;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiClassImpl;
import com.intellij.psi.search.GlobalSearchScope;
import com.mikiya.plugin.constant.ClassTypeEnum;
import com.mikiya.plugin.constant.PluginName;
import com.mikiya.plugin.dto.ClassInfoDTO;
import com.mikiya.plugin.dto.ClassMethodDTO;
import org.apache.commons.collections.CollectionUtils;

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class ProjectParse {
    private static final List<String> objSet = Arrays.asList(Object.class.getMethods()).stream().map(it -> it.getName()).collect(Collectors.toList());
    private ComplexityCalculator complexityCalculator;
    private int methodCount = 0;
    private int classCount = 0;
    private String msg = "";
    private List<ClassInfoDTO> classList = new ArrayList<ClassInfoDTO>();
    private Map<String, Integer> fetchDataMap;
    private PluginName pluginName;

    public ProjectParse(PluginName pluginName) {
        this.pluginName = pluginName;
        this.complexityCalculator = new ComplexityCalculator();
    }

    private void resetData(Project project) {
        methodCount = 0;
        classCount = 0;
        msg = String.format("class:%s，method:%s", classCount, methodCount);
        classList.clear();
        fetchDataMap = DataFetch.get(project.getName());
    }

    public int getMethodCount() {
        return methodCount;
    }

    public int getClassCount() {
        return classCount;
    }

    public List<ClassInfoDTO> getClassList() {
        return classList;
    }

    public String getMsg() {
        return msg;
    }

    public void getAllFilesInProject(Project project) {
        getAllFilesInProject(project, false);
    }

    public void getAllFilesInProject(Project project, boolean isDel) {
        if (project == null) {
            return;
        }
        resetData(project);
        PsiManager psiManager = PsiManager.getInstance(project);
        PsiDirectory baseDir = psiManager.findDirectory(project.getBaseDir());

        processDirectory(baseDir, isDel);
        msg = String.format("class:%s，method:%s", classCount, methodCount);
    }

    private void processDirectory(PsiDirectory directory, boolean isDel) {
        for (PsiElement element : directory.getChildren()) {
            if (element instanceof PsiJavaFile) {
                PsiJavaFile javaFile = (PsiJavaFile) element;
                if (!(javaFile.getFileType() instanceof JavaFileType)) {
                    continue;
                }

                ClassInfoDTO classInfo = getPath(javaFile);
                if (classInfo == null) {
                    continue;
                }

                PsiClass[] classes = javaFile.getClasses();
                for (PsiClass psiClass : classes) {
                    if (psiClass.isEnum()) {
                        classInfo.setClassType(ClassTypeEnum.ENUM);
                    }
                    if (psiClass.isInterface()) {
                        classInfo.setClassType(ClassTypeEnum.INTERFACE);
                    }
                    if (psiClass.isAnnotationType()) {
                        classInfo.setClassType(ClassTypeEnum.ANNOTATION);
                    }

                    PsiClassImpl classImpl = ((PsiClassImpl) psiClass);
                    List<PsiField> fieldList = classImpl.getOwnFields();

                    List<PsiMethod> methodList = classImpl.getOwnMethods();
                    if (CollectionUtils.isEmpty(methodList)) {
                        continue;
                    }
                    List<ClassMethodDTO> methodNameList = new ArrayList<>();
                    for (PsiMethod method : methodList) {
                        if (method.isConstructor()) {
                            continue;
                        }
                        int cyclomaticComplexity = complexityCalculator.calculateCyclomaticComplexity(method);

                        String methodName = method.getName();
                        ClassMethodDTO classMethodDTO = new ClassMethodDTO();
                        classMethodDTO.setClassName(classInfo.getClassName());
                        classMethodDTO.setMethodName(methodName);
                        classMethodDTO.setCyclomaticComplexity(cyclomaticComplexity);

//                        resetDel(classMethodDTO);

                        if (needFilter(classMethodDTO, isDel, fieldList)) {
                            continue;
                        }

                        methodNameList.add(classMethodDTO);
                        methodCount++;
                    }
                    if (CollectionUtils.isEmpty(methodNameList)) {
                        continue;
                    }
                    classCount++;
                    classInfo.setMethodList(methodNameList);
                    classList.add(classInfo);
                }
            } else if (element instanceof PsiDirectory) {
                PsiDirectory subdirectory = (PsiDirectory) element;
                processDirectory(subdirectory, isDel);
            }
        }
    }

    public void resetDel(ClassMethodDTO classMethodDTO) {
        Integer count = fetchDataMap.get(classMethodDTO.getFinalPath());
        if (count == null) {
            classMethodDTO.setDel(true);
            return;
        }
        classMethodDTO.setDel(false);
        classMethodDTO.setUsedCount(count);
    }

    private boolean needFilter(ClassMethodDTO classMethodDTO, boolean isDel, List<PsiField> fieldList) {
        List<String> getSetNames = new ArrayList<>();
        if (!CollectionUtils.isEmpty(fieldList)) {
            for (PsiField field : fieldList) {
                String name = capitalizeFirstLetter(field.getName());
                getSetNames.add("get" + name);
                getSetNames.add("set" + name);
            }
        }

        if (objSet.contains(classMethodDTO.getMethodName()) || getSetNames.contains(classMethodDTO.getMethodName()) || "builder".equalsIgnoreCase(classMethodDTO.getMethodName()) || classMethodDTO.getMethodName().contains("$")) {
            return true;
        }
        if (isDel && !classMethodDTO.getDel()) {
            return true;
        }
        return false;
    }

    private String capitalizeFirstLetter(String str) {
        if (str == null || str.isEmpty()) {
            return str;
        }

        char firstChar = Character.toUpperCase(str.charAt(0));
        return firstChar + str.substring(1);
    }

    private ClassInfoDTO getPath(PsiJavaFile javaFile) {
        List<String> result = new ArrayList<>();
        PsiDirectory containerDirectory = javaFile.getContainingDirectory();
        result.add(containerDirectory.getName());
        PsiDirectory parentDirectory = containerDirectory.getParentDirectory();
        while (true) {
            if (parentDirectory == null || "java".equalsIgnoreCase(parentDirectory.getName())) {
                break;
            }
            result.add(parentDirectory.getName());
            parentDirectory = parentDirectory.getParentDirectory();
        }
        if (CollectionUtils.isEmpty(result)) {
            return null;
        }
        StringBuilder txt = new StringBuilder();
        for (int i = result.size() - 1; i >= 0; i--) {
            txt.append(result.get(i)).append(".");
        }

        ClassInfoDTO classInfoDTO = new ClassInfoDTO(String.format("%s%s", txt, javaFile.getName().replace(".java", "")));
        return classInfoDTO;
    }


    public void clickEvent(Project project, CopyableTreeCellRenderer renderer, MouseEvent e) {
        if (e.getClickCount() != 2) {
            return;
        }

        // 双击事件处理逻辑
        JTree tree = (JTree) e.getSource();
        int selectedRow = tree.getRowForLocation(e.getX(), e.getY());
        TreePath path = tree.getPathForRow(selectedRow);
        if (path == null) {
            return;
        }
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
        Object userObject = node.getUserObject();
        if (node.getLevel() != 2) {
            return;
        }
        if (userObject == null) {
            return;
        }

        ClassMethodDTO classMethodDTO = (ClassMethodDTO) userObject;
        targetingMethod(project, renderer, classMethodDTO);
    }

    public void targetingMethod(Project project, CopyableTreeCellRenderer renderer, ClassMethodDTO classMethodDTO) {
        String className = classMethodDTO.getClassName();
        String methodName = classMethodDTO.getMethodName();
        if (project == null) {
            return;
        }
        renderer.copyToClipboard(classMethodDTO.getFinalPath());

        JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project);
        PsiClass psiClass = psiFacade.findClass(className, GlobalSearchScope.allScope(project));
        if (psiClass != null) {
            PsiMethod[] methods = psiClass.findMethodsByName(methodName, false);
            if (methods.length > 0) {
                PsiMethod method = methods[0];
                Navigatable navigatable = new OpenFileDescriptor(project, method.getContainingFile().getVirtualFile(),
                        method.getTextOffset());
                navigatable.navigate(true);
            }
        }
    }

}
